﻿window.utils = {
    /**
     * 根据父元素选择器获取父元素
     * 
     * @param {Element} target 
     * @param {String} parentClass 
     * @returns {Element}
     */
    getParentByClass(target, parentClass) {
        parentClass = this.getStringBySelector(parentClass)

        while (!this.hasClass(target, parentClass)) {
            target = target.parentNode
        }
        return target
    },

    /**
     * 去掉选择器选项
     * 
     * @param {String} selector 
     * @returns {String}
     */
    getStringBySelector(selector) {
        if (/^[\.|#]/.test(selector)) {
            selector = selector.substring(1)
        }

        return selector
    },

    /**
     * 是否是子元素
     * 
     * @param {Element} elem 
     * @param {String} parentClassString 
     * @returns {Boolean}
     */
    isChild(elem, parentClassString) {
        let checkNode = elem,
            isDocumentElem = false,
            isChild = this.hasClass(checkNode, parentClassString)

        while (!isChild && !isDocumentElem) {
            if (checkNode.tagName === 'HTML') {
                isDocumentElem = true
            }

            checkNode = checkNode.parentNode
            isChild = this.hasClass(checkNode, parentClassString)
        }

        return isChild
    },

    hasClass(elem, classString) {
        if (elem && elem.classList) {
            return elem.classList.contains(classString)
        } else {
            return false
        }
    },

    /**
     * 事件委托
     * 
     * @param {String} event 事件类型 
     * @param {Element} rootElem 根元素
     * @param {String|Array} bindElemSelector 被绑定元素的选择器
     * @param {function} callback 触发回调
     */
    on(event, rootElem, bindElemSelector, callback) {
        const _checkBind = (selectString, e) => {
            if (this.hasClass(e.target, selectString) || this.isChild(e.target, selectString)) {
                // console.log(bindElemSelector)
                callback && callback(e)
            }
        }

        rootElem.addEventListener(event, e => {
            if (bindElemSelector instanceof Array) {
                bindElemSelector.forEach(selector => _checkBind(this.getStringBySelector(selector), e))
            } else {
                _checkBind(bindElemSelector.substring(1), e)
            }
        }, false)
    }
}
class Drag {
    constructor(options) {
        if (!options.selector) {
            console.log('selector is must')
            return
        }

        this.options = options
        this.bind()
    }

    bind() {
        let moveAble = false,
            zIndex = 1,
            clientRect,
            moveElem,
            startDiffLeft,
            startDiffTop,
            endLeft,
            endTop,
            zoom = 0.5

        utils.on('mousedown', document, this.options.selector, (e) => {
            e.stopPropagation()

            moveAble = true
            moveElem = e.target
            clientRect = moveElem.getBoundingClientRect()

            startDiffLeft = e.pageX / zoom - clientRect.left
            startDiffTop = e.pageY / zoom - clientRect.top


            this.options.downCallBack && this.options.downCallBack(moveElem)

            console.info("drag mouse down")
        })

        document.addEventListener('mousemove', (e) => {
            e.stopPropagation()

            console

            if (moveAble && moveElem) {

                console.info(endLeft, e.pageX, startDiffLeft);

                endLeft = (e.pageX / zoom - startDiffLeft)
                endTop = (e.pageY / zoom - startDiffTop)
                moveElem.style.left = `${endLeft}px`
                moveElem.style.top = `${endTop}px`

                this.options.moveCallBack && this.options.moveCallBack(moveElem)
            }

            console.info("drag  mouse move")
        })

        document.addEventListener('mouseup', (e) => {
            e.stopPropagation()

            if (moveAble && moveElem) {
                moveAble = false
                moveElem = null
                console.log('drag mouse up')

                this.options.upCallBack && this.options.upCallBack(moveElem)
            }
        })
    }
}
class Resize {
    constructor(options) {
        if (!options.resizeParentSelector) {
            console.log('resizeParentSelector is must')
            return
        }

        this.options = options
        this.bind()
    }

    bind() {
        let sizAble = false,
            downX,
            downY,
            clientRect,
            resizeParentElem,
            resizeParentRect,
            classList,
            zoom = 0.5

        utils.on('mousedown', document.body, ['.resize'], (e) => {
            e.stopPropagation()
            sizAble = true

            downX = e.pageX
            downY = e.pageY

            classList = utils.getParentByClass(e.target, '.resize').classList
            resizeParentElem = utils.getParentByClass(e.target, this.options.resizeParentSelector)
            resizeParentRect = resizeParentElem.getBoundingClientRect()
            clientRect = e.target.getBoundingClientRect()

            console.log('resize mouse down')
        })

        document.addEventListener('mousemove', (e) => {
            e.stopPropagation()

            if (sizAble && resizeParentElem) {
                let diffX = (e.pageX - downX) / zoom,
                    diffY = (e.pageY - downY) / zoom

                const { width, height, left, top, right, bottom } = resizeParentRect
                const resizeLineDot = {
                    'line-xt': {
                        isSelf: classList.contains('line-xt'),
                        width: `${width}px`,
                        height: `${height - diffY}px`,
                        top: `${top + diffY}px`
                    },
                    'line-xb': {
                        isSelf: classList.contains('line-xb'),
                        width: `${width}px`,
                        height: `${height + diffY}px`,
                        top: `${top}px`,
                        left: `${left}px`
                    },
                    'line-yl': {
                        isSelf: classList.contains('line-yl'),
                        width: `${width - diffX}px`,
                        height: `${height}px`,
                        top: `${top}px`,
                        left: `${left + diffX}px`
                    },
                    'line-yr': {
                        isSelf: classList.contains('line-yr'),
                        width: `${width + diffX}px`,
                        height: `${height}px`,
                        top: `${top}px`,
                        left: `${left}px`
                    },
                    'dot-lt': {
                        isSelf: classList.contains('dot-lt'),
                        width: `${width - diffX}px`,
                        height: `${height - diffY}px`,
                        top: `${top + diffY}px`,
                        left: `${left + diffX}px`
                    },
                    'dot-rt': {
                        isSelf: classList.contains('dot-rt'),
                        width: `${width + diffX}px`,
                        height: `${height - diffY}px`,
                        top: `${top + diffY}px`,
                        left: `${left}px`
                    },
                    'dot-lb': {
                        isSelf: classList.contains('dot-lb'),
                        width: `${width - diffX}px`,
                        height: `${height + diffY}px`,
                        top: `${top}px`,
                        left: `${left + diffX}px`
                    },
                    'dot-rb': {
                        isSelf: classList.contains('dot-rb'),
                        width: `${width + diffX}px`,
                        height: `${height + diffY}px`,
                        top: `${top}px`,
                        left: `${left}px`
                    }
                }

                Object.keys(resizeLineDot).forEach(item => {
                    if (resizeLineDot[item].isSelf) {
                        Object.keys(resizeLineDot[item]).forEach(key => {
                            resizeParentElem.style[key] = resizeLineDot[item][key]
                        })
                    }
                })
            }

            //console.log('resize mouse move')
        })

        document.addEventListener('mouseup', (e) => {
            e.stopPropagation()

            if (sizAble && resizeParentElem) {
                sizAble = false
                resizeParentElem = null
                console.log('resize mouse up')
            }
        })
    }
}
let lines = {
    xt: null,
    xc: null,
    xb: null,
    yl: null,
    yc: null,
    yr: null,
}

// 置入参考线
for (let p in lines) {
    let node = lines[p] = document.createElement('div')

    node.classList.add('ref-line', p)
    node.style.cssText = `display:none;opacity:0.7;position:absolute;background:#4DAEFF;z-index:199111250;${p[0] === 'x' ? 'width:100%;height:1px;left:0;' : 'width:1px;height:100%;top:0;'}`

    // 挂上一些辅助方法
    node.show = function () {
        this.style.display = 'block'
    }
    node.hide = function () {
        this.style.display = 'none'
    }
    node.isShow = function () {
        return this.style.display !== 'none'
    }
    let s = document.getElementById("mydata");
    s.appendChild(node)
}

class RefLine {
    constructor(options = {}) {
        this.options = Object.assign({
            gap: 3
        }, options)
    }

    /**
     * @param dragNode {Element} 拖拽元素的原生node
     * @param checkNodes {String|Element} 选择器 或者 原生node集合
     */
    check(dragNode, checkNodes) {
        checkNodes = typeof checkNodes === 'string' ? document.querySelectorAll(checkNodes) : checkNodes
        let dragRect = dragNode.getBoundingClientRect()

        this.uncheck()
        Array.from(checkNodes).forEach((item) => {
            item.classList.remove('ref-line-active')

            if (item === dragNode) return
            let {top, height, bottom, left, width, right} = item.getBoundingClientRect()
            let dragWidthHalf = dragRect.width / 2
            let itemWidthHalf = width / 2
            let dragHeightHalf = dragRect.height / 2
            let itemHeightHalf = height / 2

            let conditions = {
                top: [
                    // xt-top
                    {
                        isNearly: this._isNearly(dragRect.top, top),
                        lineNode: lines.xt,
                        lineValue: top,
                        dragValue: top
                    },
                    // xt-bottom
                    {
                        isNearly: this._isNearly(dragRect.bottom, top),
                        lineNode: lines.xt,
                        lineValue: top,
                        dragValue: top - dragRect.height
                    },
                    // xc
                    {
                        isNearly: this._isNearly(dragRect.top + dragHeightHalf, top + itemHeightHalf),
                        lineNode: lines.xc,
                        lineValue: top + itemHeightHalf,
                        dragValue: top + itemHeightHalf - dragHeightHalf
                    },
                    // xb-top
                    {
                        isNearly: this._isNearly(dragRect.bottom, bottom),
                        lineNode: lines.xb,
                        lineValue: bottom,
                        dragValue: bottom - dragRect.height
                    },
                    // xb-bottom
                    {
                        isNearly: this._isNearly(dragRect.top, bottom),
                        lineNode: lines.xb,
                        lineValue: bottom,
                        dragValue: bottom
                    },
                ],
                left: [
                    // yl-left
                    {
                        isNearly: this._isNearly(dragRect.left, left),
                        lineNode: lines.yl,
                        lineValue: left,
                        dragValue: left
                    },
                    // yl-right
                    {
                        isNearly: this._isNearly(dragRect.right, left),
                        lineNode: lines.yl,
                        lineValue: left,
                        dragValue: left - dragRect.width
                    },
                    // yc
                    {
                        isNearly: this._isNearly(dragRect.left + dragWidthHalf, left + itemWidthHalf),
                        lineNode: lines.yc,
                        lineValue: left + itemWidthHalf,
                        dragValue: left + itemWidthHalf - dragWidthHalf
                    },
                    // yr-left
                    {
                        isNearly: this._isNearly(dragRect.right, right),
                        lineNode: lines.yr,
                        lineValue: right,
                        dragValue: right - dragRect.width
                    },
                    // yr-right
                    {
                        isNearly: this._isNearly(dragRect.left, right),
                        lineNode: lines.yr,
                        lineValue: right,
                        dragValue: right
                    },
                ]
            }

            for (let key in conditions) {
                // 遍历符合的条件并处理
                conditions[key].forEach((condition) => {
                    if (!condition.isNearly) return

                    item.classList.add('ref-line-active')
                    dragNode.style[key] = `${condition.dragValue}px`
                    condition.lineNode.style[key] = `${condition.lineValue}px`
                    condition.lineNode.show()
                })
            }
        })
    }

    uncheck() {
        Object.values(lines).forEach((item) => item.hide())
        Array.from(document.querySelectorAll('.ref-line-active')).forEach((item) => item.classList.remove('ref-line-active'))
    }

    _isNearly(dragValue, targetValue) {
        return Math.abs(dragValue - targetValue) <= this.options.gap
    }
}

if (typeof module === 'object') {
    module.exports = RefLine
}




